package com.zgg.myjwt.untils;

import com.zgg.myjwt.pojo.SysUser;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @author ant_Zhou
 * @email 1641583112@qq.com
 * @create 2020-05-15 20:44
 */
public class JwtUtil {
    private static final String KEY = "asdxkdsds";

    /**
     * 用户登录后生成jwt
     * 使用hs256算法 私钥是用户的密码
     * <p>
     * iss：发行人
     * exp：到期时间
     * sub：主题
     * aud：用户
     * nbf：在此之前不可用
     * iat：发布时间
     * jti：JWT ID用于标识该JWT
     *
     * @param ttlMillis jwt的过期时间
     * @param user      登录成功的user对象
     * @return
     */
    public static String createJWT(long ttlMillis, SysUser user) {
        //用来指点签名用的算法，是header部分，jwt已经将算法进行了封装
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        //生成jwt的时间
        Long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);

        //创建payload (用来保存用户信息的部分)的私有声明（根据特定的业务需要添加，如果要拿这个做验证，一般是需要和jwt的接收方提前沟通好验证方式的）
        Map<String, Object> claims = new HashMap<String, Object>();
        claims.put("id", user.getUserId());
        claims.put("account", user.getAccount());
        claims.put("password", user.getPassword());

        //生成签名的时候使用的秘钥secret,这个方法本地封装了的，一般可以从本地配置文件中读取，切记这个秘钥不能外露哦。
        //它就是你服务端的私钥，在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
        //注意：这个字段可以自己设置
        //String key =user.getPassword();

        //生成签发人
        String subject = user.getAccount();

        //下面就是在为payload添加各种标准声明和私有声明了
        //这里其实就是new一个JwtBuilder，设置jwt的body
        JwtBuilder builder = Jwts.builder()
                //如果有私有声明，一定要先设置这个自己创建的私有的声明，这个是给builder的claim赋值，
                // 一旦写在标准的声明赋值之后，就是覆盖了那些标准的声明的(用来携带数据的)
                .setClaims(claims)
                //设置jti(JWT ID)：是JWT的唯一标识，根据业务需要，这个可以设置为一个不重复的值，主要用来作为一次性token,从而回避重放攻击。
                .setId(UUID.randomUUID().toString())
                //签发的时间设置
                .setIssuedAt(now)
                //代表这个JWT的主体，即它的所有人，这个是一个json格式的字符串，可以存放什么userid，roldid之类的，作为什么用户的唯一标志。
                .setSubject(subject)
                //设置签名使用的签名算法和签名使用的秘钥
                .signWith(signatureAlgorithm, KEY);
        if (ttlMillis > 0) {
            //计算什么时候过期
            long expMillis = nowMillis + ttlMillis;
            Date exp = new Date(expMillis);
            //设置过期时间
            builder.setExpiration(exp);
        }
        //
        return builder.compact();
    }

    /**
     * token 的解密
     *
     * @param token 加密后的token
     * @param user  用户对象
     * @return
     */
    public static Claims paserJWT(String token, SysUser user) {
        //签名的密钥，和生成的签名密钥一模一样
        //String key = user.getPassword();
        //得到DefaultJwtParer() 得到声明中的数据
        Claims claims = Jwts.parser()
                //设置签名的密钥
                .setSigningKey(KEY)
                //设置需要解析的jwt
                .parseClaimsJws(token).getBody();
        return claims;
    }

    /**
     * 校验token
     * 在这里可以使用官方的校验，我这里校验的是token中携带的密码于数据库一致的话就校验通过
     *
     * @param token
     * @param user
     * @return
     */
    public static Boolean isVerify(String token, SysUser user) {
        //签名秘钥，和生成的签名的秘钥一模一样
        //String key = user.getPassword();

        //得到DefaultJwtParser
        Claims claims = Jwts.parser()
                //设置签名的秘钥
                .setSigningKey(KEY)
                //设置需要解析的jwt
                .parseClaimsJws(token).getBody();

        //用来校验密码
        if (claims.get("password").equals(user.getPassword())) {
            return true;
        }
        return false;
    }


}
